#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>

#define N     4 // 每个图形方格边长
#define BASE  7 // 7种图形
#define FORM  4 // 4种变换

#define ROW	20 // 地图行数
#define COL	14 // 地图列数

#define BAND	2 // 边界和停落的方块
#define BLOCK   1 // 运动的方块

// 图形结构
struct blocks {
	int space[N][N];
} blocks[BASE][FORM];

// 地图数组
int tetris[ROW][COL];
int row, col;

// 颜色枚举
enum {BLACK, RED, GREEN, YELLOW, BLUE, PURPLE, DARKGREEN};
// 操作枚举
enum {CHANGE, LEFT=1, RIGHT, DOWN};

int colour; // 当前方块颜色
int colour_arr[ROW][COL]; // 每个方块颜色
int colour_next;

int base; // 当前方块类型
int form; // 当前方块变换
int next_form;
int base_next; // 下一个方块类型

struct termios new, old;

void initBlocks();
void showBg();
void selectColour(int select);
void writeInBlock();
void printBlock();
void run();
void execAction(int action);
void judgeLast();
int judgeMove(int move);
void debug();
void nextBlock();
void judgeLine();
int judgeChange();
int judgeOver();

void alarmHandler(int s)
{
	alarm(1);
	// debug();
	if (judgeMove(DOWN) == 1) {
		execAction(DOWN);
	} else {
		judgeLast(); // 停留棋盘
		alarm(0);
		run();
	}
}

int main(void)
{
	char c;

	// 信号使图形自由下落
	signal(SIGALRM, alarmHandler);

	// 初始化
	initBlocks();
	// 绘制背景
	showBg();

	// 关闭光标显示
	printf("\033[?25l");

	// 随机产生方块图形
	srand(time(NULL));
	base_next = rand() % BASE;
	// 随机确定当前方块颜色
	colour_next = rand() % 7;
	next_form = next_form % FORM;
	run();

	// 关闭标准输入回显和缓存
	tcgetattr(0, &old);
	new = old;
	new.c_lflag = new.c_lflag & ~(ICANON | ECHO);

	tcsetattr(0, TCSANOW, &new);

	while (1) {
		c = getchar(); 
		switch (c) {
			case 'q':
			case 'Q':
				alarm(0);
				printf("\033[10;20 Quit");
				break;
			case 'a':
			case 'A':
				// 左移
				if (judgeMove(LEFT)) {
					execAction(LEFT);
				}
			break;
			case 'd':
			case 'D':
				// 右移
				if (judgeMove(RIGHT)) {
					execAction(RIGHT);
				}
				break;
			case 's':
				// 加速下落
				if (judgeMove(DOWN)) {
					execAction(DOWN);
				} else {
					judgeLast(); // 停留棋盘
					alarm(0);
					run();
				}
				break;
			case 'w':
				// 变换
				if (judgeChange()) {
					execAction(CHANGE);
				}
			break;
		}
	}
	return 0;
}

/*
	运行游戏
*/
void run()
{
	alarm(1);
	// 方块初始行列
	row = 2;
	col = 5;

	base = base_next;
	colour = colour_next;
	form = next_form;
	// 随机产生方块图形
	srand(time(NULL));
	base_next = rand() % BASE;
	// 随机产生方块颜色
	colour_next = rand() % 7;
	next_form = next_form % FORM;

	// 判断游戏是否结束
	if (judgeOver()) {
		printf("\033[15;20HGame Over");
		tcsetattr(0, TCSANOW, &old);
		exit(0); // 进程终止
	}
	
	writeInBlock(); // 方块写入地图
}

/*
	初始化方块
*/
void initBlocks()
{
	int row, col;
	int temp[N][N] = {};
	int base, form;
 
	// blocks[0]
	//  0000
	//  0110
	//  0110
	//  0000
	for (row = 1; row <= 2; row++) {
		for (col = 1; col <= 2; col++) {
			blocks[0][0].space[row][col] = 1;
		}
	}

	// blocks[1] blocks[2]
	//  0000      0000
	//  1100      0110
	//  0110      1100
	//  0000      0000
	for (col = 0; col < 2; col++) {
		blocks[1][0].space[1][col] = 1;
		blocks[1][0].space[2][col + 1] = 1;
		blocks[2][0].space[1][col + 1] = 1;
		blocks[2][0].space[2][col] = 1;
	}

	// blocks[3] blocks[4]
	//  0000      0000
	//  1100      0110
	//  0100      0100
	//  0100      0100
	for (row = 1; row < 4; row++) {
		blocks[3][0].space[row][1] = 1;
		blocks[3][0].space[1][0] = 1;
		blocks[4][0].space[row][1] = 1;
		blocks[4][0].space[1][2] = 1;
	}

	// blocks[5]
	//  0100
	//  0100
	//  0100
	//  0100
	for (row = 0; row < 4; row++) {
		blocks[5][0].space[row][1] = 1;
	}

	// blocks[6]
	//  0000
	//  0100
	//  1110
	//  0000
	for (col = 0; col < 3; col++) {
		blocks[6][0].space[1][1] = 1;
		blocks[6][0].space[2][col] = 1;
	}

	for (base = 0; base < BASE; base++) {
		for (form = 0; form < FORM - 1; form++) {
			// 保存变换前图形
			for (row = 0; row < N; row++) {
				for (col = 0; col < N; col++) {
					temp[row][col] = blocks[base][form].space[row][col];
				}
			}

			// 变换
			for (row = 0; row < N; row++) {
				for (col = 0; col < N; col++) {
					blocks[base][form + 1].space[row][col] = temp[N - 1 - col][row];
				}
			}
		}
	}
}

/*
	绘制背景
*/
void showBg()
{ 
	int i, j, k;

	printf("\033[2J"); // 清屏
	// ROW COL
	printf("\033[5;10H\033[45m--====================-------------------\033[0m\n");

	for (i = 0; i < ROW - 4; i++) {
		printf("\033[%d;10H\033[45m||\033[0m", 6+i);
		for (j = 0; j < COL-4; j++) {
			printf(" ");
		}
		printf("\033[45m||");
		if (i == 6 || i == 10) {
			printf("\033[45m---------------||\033[0m\n");
		} else {
			printf("\033[0m\t\t\033[45m||\033[0m\n");
		}
	}
	printf("\033[22;10H\033[45m--====================-------------------\033[0m\n");
	
	// 初始化地图数组边界
	for (i = 0, j = ROW - 2; i < 2 && j < ROW; i++, j++) {
		for(k = 0; k < COL; k++) {
			tetris[i][k] = BAND;
			tetris[j][k] = BAND;
		}
	}

	for (i = 2; i < ROW - 2; i++) {
		for (j = 0, k = COL - 2; j < 2 && k < COL; j++, k++) {
			tetris[i][j] = BAND;
			tetris[i][k] = BAND;
		}
	}
}

/*
	选择颜色
*/
void selectColour(int select)
{
	switch (select) {
		case BLACK:
			printf("\033[40m");
			break;
		case RED:
			printf("\033[41m");
			break;
		case GREEN:
			printf("\033[42m");
			break;
		case YELLOW:
			printf("\033[43m");
			break;
		case BLUE:
			printf("\033[44m");
			break;
		case PURPLE:
			printf("\033[45m");
			break;
		case DARKGREEN:
			printf("\033[46m");
			break;
	}
}

/*
	绘制地图
*/
void printBlock()
{
	int i, j;

	printf("\033[6;12H");

	for (i = 2; i < ROW - 2; i++) {
		for (j = 2; j < COL - 2; j++) {
			if (tetris[i][j] == BLOCK) {
				// 正在下落的方块
				selectColour(colour);
				printf("[]\033[0m");
			} else if (tetris[i][j] == BAND) {
				// 已经下落后停留的方块
				selectColour(colour_arr[i][j]);
				printf("[]\033[0m");
			} else {
				printf("  ");
			}
				
		}
		printf("\n\033[11C"); // 让每一行起始列都从12列开始
	}

	nextBlock();
}

/*
	方块写入地图
*/
void writeInBlock()
{
	int i, j;

	for (i = 0; i < N; i++) {
		for (j = 0; j < N; j++) {
			if (tetris[row + i][col + j] != BAND) {
				// 防止覆盖已下落的方块
				tetris[i + row][j + col] = blocks[base][form].space[i][j];
			}
		}
	}
	printBlock();
}

/*
	判断方块能否运动
	能返回1，不能返回0
*/
int judgeMove(int move)
{
	// 移动的行列
	int f_row ,f_col;
	int i, j;

	f_row = f_col = 0;

	switch (move) {
		case LEFT:
			f_col = -1;
			break;
		case RIGHT:
			f_col = 1;
			break;
		case DOWN:
			f_row = 1;
			break;
		default:
			break;
	}
	for (i = 0; i < N; i++) {
		for (j = 0; j < N; j++) {
			if (tetris[row + i][col + j] == 1) {
				if (tetris[row + i + f_row][col + j + f_col] == BAND)
				return 0;
			}
		}
	}
	
	return 1;
}

/*
	取消上一个状态
*/
void cancelState(int state)
{
	int i, j;
	int last_row, last_col;

	last_row = last_col = 0;
	
	switch (state) {
		case CHANGE:
			break;
		case LEFT:
			last_col = 1; 
			break;
		case RIGHT:
			last_col = -1;
			break;
		case DOWN:
			last_row = -1;
			break;
		default:
			break;
	}

	for (i = 0; i < N; i++) {
		for (j = 0; j < N; j++) {
			if (tetris[i + row + last_row][j + col + last_col] != BAND)
				tetris[i + row + last_row][j + col + last_col] = 0;
		}
	}
	printBlock();
}

/*
	方块停留棋盘
*/
void judgeLast()
{
	int i, j;

	for (i = 2;i < ROW - 2; i++) {
		for (j = 2;j < COL - 2; j++) {
			if (tetris[i][j] == BLOCK) { // 运动的方块
				tetris[i][j] = BAND;
				colour_arr[i][j] = colour;
			}
		}
	}

	// 判断是否消行
	judgeLine();

	printBlock();
}

/*
	执行动作
*/
void execAction(int action)
{
	switch (action) {
		case CHANGE:
			form = (form + 1) % FORM; 
			cancelState(action);
			break;
		case LEFT:
			col--;
			cancelState(action);
			break;
		case RIGHT:
			col++;
			cancelState(action);
			break;
		case DOWN:
			row++;
			cancelState(action);
			break;
		default:
			break;
	}
	writeInBlock();
}

/*
	调试功能
*/
void debug()
{
	int i, j;

	printf("\033[5;58H");

	for(i = 0; i < ROW; i++) {
		for (j = 0; j < COL; j++) {
			printf("%d\033[0m", tetris[i][j]);
		}
		printf("\n\033[57C");
	}
}

/*
	显示下一个图形
*/
void nextBlock()
{
	int i, j;

	printf("\033[7;38H");

	for (i = 0; i < N; i++) {
		for (j = 0; j < N; j++) {
			if (blocks[base_next][next_form].space[i][j]) {
				selectColour(colour_next);
				printf("[]\033[0m");
			} else {
				printf("  ");
			}
		}
		printf("\n\033[37C");
	}
}

void execRemoveLine(int r)
{
	int i, j;

	for (i = r; i > 2; i--) {
		for (j = 2; j < COL - 2; j++) {
			tetris[i][j] = tetris[i - 1][j];
		}
	}

	for (j = 2; j < COL - 2; j++) {
		tetris[2][j] = 0; // 棋盘最上方
	}
}

/*
	判断消行
*/
void judgeLine()
{
	int i, j; 
	int count = 0;
	int t;

	for (i = ROW - 3; i >= 2; i--) {
		count = 0;
		for (j = 2; j < COL - 2; j++) {
			if (tetris[i][j] == BAND) 
				count++;
		}
		if (count == COL - 4) { 
			t = i; // 一行满了执行消行
			execRemoveLine(t);
			i = t + 1; // 重落下来的行继续判断
		}
	}
}

/*
	判断变换
*/
int judgeChange()
{
	int i, j; 
	int tmp;
	int cnt = 0;

	// debug();

	tmp = (form + 1) % FORM;

	for (i = 0; i < N; i++) {
		for (j = 0; j < N; j++) {
			if (blocks[base][tmp].space[i][j]) {
				if (tetris[row + i][col + j] != 2) {
					// 当前下标方块不与边界或已停落方块重叠
					cnt++;
					printf("\033[30;12Hcnt:%d", cnt);
					if (cnt == 4) // 旋转的新图形不会重叠
						return 1;
				}
			}
		}
	}

	return 0;
}

/*
	判断游戏结束
*/
int judgeOver()
{
	int i, j;

	// 第三行有方块
	for (j = 2; j < COL - 2; j++) {
		if (tetris[2][j] == BAND)
			return 1; 
	}

	// 下一个图形进入不了地图
	for (i = 0; i < N; i++) {
		for (j = 0; j < N; j++) {
			if (blocks[base][form].space[i][j] == 1) {
				if (tetris[row + i][col + j] == 2)
					return 1;
			}
		}
	}
	return 0;
}
